﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Net;
using System.Net.Sockets;
using TradingPlatform;
using System.Windows.Forms;
using System.Threading;
using ALib.Data.IO;

namespace TradingPlatform
{
    class StateObject
    {
        public Socket Socket = null;
        public const int BufferSize = 1024;
        public byte[] buffer = new byte[BufferSize];
        public string Result;
        public Hashtable SymbolToTickerMap;

        public TradingGuiBase Gui = null;
    }

    public class Comm
    {
        public enum Status
        {
            Disconnected,
            Connecting,
            Connected
        }
        public Comm()
        {
            mSocket = null;
            mGui = null;
            mStatus = Status.Disconnected;
            loadTickerSymbolMap();
        }

        public bool IsConnecting() { return mStatus == Status.Connecting; }
        public bool IsConnected() { return mStatus == Status.Connected; }
        public bool IsDisconnected() { return mStatus == Status.Disconnected; }

        public void setGui(TradingGuiBase gui)
        {
            mGui = gui;
        }

        public void DoConnect(string addr, int port) {
            try
            {
                mAddr = addr; mPort = port;
                mGui.DisplayMessage("Init connection to " + mAddr + "@" + mPort);
                if (mSocket != null && mSocket.Connected)
                    return;

                mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint server = new IPEndPoint(IPAddress.Parse(mAddr), mPort);
                //AsyncCallback oc = new AsyncCallback(OnConnect);
                //mSocket.BeginConnect (server, oc, mSocket);
                mSocket.Connect(server);
                mGui.DisplayMessage("Start connecting to Comm now " + Thread.CurrentThread.ManagedThreadId);
                if (mSocket.Connected)
                {
                    mStatus = Status.Connected;
                    mSocket.Blocking = false;

                    mGui.Invoke(mGui.mCommConnectedDelegate, "Comm connected");
                    DoReceive(/*mSocket, mGui*/);
                }
                else
                {
                    mGui.Invoke(mGui.mCommConnectedDelegate, "Comm not connected");
                    mStatus = Status.Disconnected;
                }
            }
            catch (Exception e)
            {
                mGui.DisplayMessage("Exception occured when connecting to comm. Err=" + e.ToString());
            }
        }

        private void DoSend(string data)
        {
            if (mStatus == Status.Connected )
            {
                mSocket.Send(Encoding.ASCII.GetBytes(data));
                mGui.DisplayMessage("Sending data to comm: " + data);
            }
        }

        public void OnConnect( IAsyncResult ar ) {
            //mKernal.DisplayStatus("Connected");
            //Console.WriteLine("OnConnect Thread: " + Thread.CurrentThread.ManagedThreadId);
            Socket socket = (Socket)ar.AsyncState;
            if (socket.Connected)
            {
                mStatus = Status.Connected;
                mGui.Invoke(mGui.mCommConnectedDelegate, "Comm connected");
            }
            else
            {
                mStatus = Status.Disconnected;
                mGui.Invoke(mGui.mCommDisconnectedDelegate, "Comm not conneted");
            }

        }

        private void DoReceive(/*Socket socket, TradingGuiBase gui*/)
        {
            try
            {
                StateObject stateObject = new StateObject();
                stateObject.Socket = mSocket;
                stateObject.Gui = mGui;
                stateObject.Result = "";
                stateObject.SymbolToTickerMap = mSymbolToTicker;
                mSocket.BeginReceive(stateObject.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceivedCallback), stateObject);
            }
            catch (Exception)
            {
            }
        }

        private static void ReceivedCallback( IAsyncResult ar ) {
            try
            {
                StateObject state = (StateObject) ar.AsyncState;
                Socket socket = state.Socket;
                int byteRead = socket.EndReceive(ar);
                if (byteRead > 0)
                {
                    state.Result += Encoding.ASCII.GetString(state.buffer, 0, byteRead);
                    List<CommMessage> msgList = new List<CommMessage>();
                    Hashtable map = state.SymbolToTickerMap;
                    while (state.Result.Length > 0)
                    {
                        if (MarketDataBestLevelMessage.IsType(state.Result))
                        {
                            MarketDataBestLevelMessage data = MarketDataBestLevelMessage.Parse(ref state.Result);
                            if (data != null && setTickerName(ref data.SymbolField, map) )
                                msgList.Add(data);
                        }
                        else if (MarketDataTradeMessage.IsType(state.Result))
                        {
                            MarketDataTradeMessage data = MarketDataTradeMessage.Parse(ref state.Result);
                            if (data != null && setTickerName(ref data.SymbolField, map))
                                msgList.Add(data);
                        }
                        else if (OrderStatusMessage.IsType(state.Result))
                        {
                            OrderStatusMessage data = OrderStatusMessage.Parse(ref state.Result);
                            if (data != null && setTickerName(ref data.SymbolField, map))
                                msgList.Add(data);
                        }
                        else if (LoginStatusMessage.IsType(state.Result))
                        {
                            LoginStatusMessage data = LoginStatusMessage.Parse(ref state.Result);
                            if (data != null) 
                                msgList.Add(data);
                        }
                        else if (SubscribeMarketDataStatusMessage.IsType(state.Result))
                        {
                            SubscribeMarketDataStatusMessage data = SubscribeMarketDataStatusMessage.Parse(ref state.Result);
                            if (data != null && setTickerName(ref data.Symbol, map))
                                msgList.Add(data);
                        }
                    }
                    if (msgList.Count > 0)
                    {
                        TradingGuiBase gui = state.Gui;
                        gui.Invoke(gui.mCommDataReadDelegate, msgList);
                    }

                }

                socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceivedCallback), state);
            }
            catch (Exception e)
            {
                string err = e.ToString();
            }
        }

        public void Login(string name)
        {
            LoginMessage msg = new LoginMessage();
            msg.NameField.Value = name;
            string loginMessage = LoginMessage.Format(msg);
            DoSend(loginMessage);
        }

        public void InsertOrder(int orderID, string ticker, double price, int quantity, CommOrderTypeField.Type type)
        {
            if (mStatus == Status.Connected)
            {
                if(mTickerToSymbol.Contains(ticker)) {
                    string symbol = (string) mTickerToSymbol[ticker];
                    OrderMessage order = new OrderMessage();
                    order.OrderIDField.Value = orderID;
                    order.SymbolField.Value = symbol;
                    order.OrderActionField.Value = (int)CommOrderActionField.Type.Insert;
                    order.PriceField.Value = price;
                    order.SizeField.Value = Math.Abs(quantity);
                    order.SideField.Value = (int)(quantity > 0 ? CommSideField.Side.Buy : CommSideField.Side.Sell);
                    order.OrderTypeField.Value = (int)type;
                    string orderMessage = OrderMessage.Format(order);
                    DoSend(orderMessage);
                }
            }
        }

        public void CancelOrder(int orderID)
        {
            if (mStatus == Status.Connected)
            {
                OrderMessage order = new OrderMessage();
                order.OrderIDField.Value = orderID;
                order.OrderActionField.Value = (int)CommOrderActionField.Type.Cancel;
                string orderMessage = OrderMessage.Format(order);
                DoSend(orderMessage);
            }
        }

        public void ReqMarketData(string ticker, bool onOff)
        {
            if (mStatus == Status.Connected)
            {
                if (mTickerToSymbol.Contains(ticker))
                {
                    string symbol = (string)mTickerToSymbol[ticker];
                    SubscribeMarketDataMessage sub = new SubscribeMarketDataMessage();
                    sub.SymbolField.Value = symbol;
                    sub.FlagField.Value = onOff ? (int)CommFlagField.Flag.On : (int)CommFlagField.Flag.Off;
                    string subMessage = SubscribeMarketDataMessage.Format(sub);
                    DoSend(subMessage);
                }
            }
        }

        private void loadTickerSymbolMap()
        {
            mTickerToSymbol = new Hashtable();
            mSymbolToTicker = new Hashtable();

            Configuration configuration = new Configuration(@"Configuration\TickerSymbolMap.txt");
            List<string> tickerNames = configuration.GetAttributeNames();
            string ticker, symbol;
            for (int i = 0; i < tickerNames.Count; i++)
            {
                ticker = tickerNames[i];
                symbol = configuration.GetString(ticker);
                mTickerToSymbol.Add(ticker, symbol);
                mSymbolToTicker.Add(symbol, ticker);
            }
        }

        private static bool setTickerName(ref CommSymbolField field, Hashtable symbolToTickerMap)
        {
            string symbol = field.StringValue.Trim();
            if (symbolToTickerMap.Contains(symbol))
            {
                field.Value = (string)symbolToTickerMap[symbol];
                return true;
            }
            else
            {
                return false;
            }
        }

        private String mAddr;
        private int mPort;
        private Socket mSocket;
        private TradingGuiBase mGui;
        private Status mStatus;
        private Hashtable mTickerToSymbol;
        private Hashtable mSymbolToTicker;

    }
}
